/**
 * Copyright 2012 BTChip 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.btchip.protocol;

import java.io.IOException;

import com.codeminders.hidapi.HIDDevice;

public class BTChip {
	
	private static final int DEFAULT_TIMEOUT = 10000;
	private static final int MAX_BLOCK = 64;
	private static final int SW_DATA = 0x61;
		
	private HIDDevice device;
	private boolean open;
	private int timeout;
		
	public BTChip(HIDDevice device) {
		this.device = device;
		this.open = true;
		this.timeout = DEFAULT_TIMEOUT;		
	}
	
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	private byte[] exchange(byte[] apdu, boolean skipResponse) throws BTChipCommunicationException,
			BTChipInvalidStatusWordException {
		byte block[] = new byte[MAX_BLOCK + 1];
		if (!open) {
			throw new BTChipCommunicationException("Device has been closed");
		}
		try {
			byte[] response = null;
			// Send the full APDU
			int offset = 0;
			int size;
			while (offset != apdu.length) {
				int blockSize = ((offset + MAX_BLOCK) < apdu.length ? MAX_BLOCK : apdu.length - offset);
				System.arraycopy(apdu, offset, block, 1, blockSize);
				device.write(block);
				offset += blockSize;
			}
			if (!skipResponse) {
				// Read the response
				size = device.readTimeout(block, timeout);
				if (size != MAX_BLOCK) {
					throw new BTChipCommunicationException("Protocol error"); 
				}
				if (block[0] != SW_DATA) { // only the Status Word is available
					response = new byte[2];
					System.arraycopy(block, 0, response, 0, 2);
				}
				else {
					int responseSize = (block[1] & 0xff);
					if (responseSize == 0) {
						responseSize = 0x100; // T=0 compliance
					}
					responseSize += 2; // include the Status Word
					response = new byte[responseSize];
					offset = 0;
					while (offset != responseSize) {
						int startOffset = (offset != 0 ? 0 : 2);
						int blockSize = ((offset + MAX_BLOCK - startOffset) < responseSize ? (MAX_BLOCK - startOffset) : responseSize - offset);
						System.arraycopy(block, startOffset, response, offset, blockSize);
						offset += blockSize;
						if (offset == responseSize) {
							break;
						}
						size = device.readTimeout(block, timeout);
						if (size != MAX_BLOCK) {
							throw new BTChipCommunicationException("Protocol error"); 
						}					
					}
				}
			}
			return response;
		}
		catch (IOException e) {
			throw new BTChipCommunicationException(e);
		}	
	}
	
	public byte[] exchange(byte[] apdu) throws BTChipCommunicationException, BTChipInvalidStatusWordException {
		return exchange(apdu, false);
	}
	
	
	public void close() {
		try {
			device.close();
			open = false;
		}
		catch(IOException e) {			
		}
	}
	

}
